﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace Common.Cryptography
{
    public static class AESHelper
    {

        #region Global Var(s)

        private static Aes AesEninge = null;
        private const int AesKeySize = 32;
        private const int AesIVSize = 16;
        //internal static byte[] key = null;
        //internal static byte[] iv = null;

        private static MemoryStream ms = null;
        private static CryptoStream cs = null;
        private static BinaryWriter bw = null;
        private static BinaryReader br = null;

        #endregion

        #region Basic

        public static void init(string strPassword)
        {
            if (String.IsNullOrEmpty(strPassword))
                throw new ArgumentNullException("strPassword");

            AesEninge = Aes.Create();
            AesEninge.Key = CryptographyHelper.GetKeyFromPassword(strPassword, AesKeySize);
            AesEninge.IV = CryptographyHelper.GetKeyFromPassword(strPassword, AesIVSize);
        }

        public static void free()
        {
            if (AesEninge.isNull() == false)
                AesEninge.Clear();
        }

        //public static void SaveFiles(string strFilename)
        //{
        //    if (AesEninge.isNull())
        //        throw new ArgumentNullException("Must call init frist!");
        //    if (String.IsNullOrEmpty(strFilename))
        //        throw new ArgumentNullException("strPathName");
        //    if (Directory.Exists(Path.GetDirectoryName(strFilename)) == false)
        //        throw new DirectoryNotFoundException();

        //    string strdir = Path.GetDirectoryName(strFilename);
        //    string fname = Path.GetFileNameWithoutExtension(strFilename);

        //    File.WriteAllBytes(strdir + fname + ".aes", AesEninge.Key);
        //    File.WriteAllBytes(strdir + fname + ".aesIV", AesEninge.IV);
        //}

        //public static void ReadFiles(string strFilename)
        //{
        //    if (AesEninge.isNull())
        //        throw new ArgumentNullException("Must call init frist!");
        //    if (String.IsNullOrEmpty(strFilename))
        //        throw new ArgumentNullException("strPathName");
        //    if (Directory.Exists(Path.GetDirectoryName(strFilename)) == false)
        //        throw new DirectoryNotFoundException();

        //    string strdir = Path.GetDirectoryName(strFilename);
        //    string fname = Path.GetFileNameWithoutExtension(strFilename);

        //    AesEninge.Key = File.ReadAllBytes(strdir + fname + ".aes");
        //    AesEninge.IV = File.ReadAllBytes(strdir + fname + ".aesIV");
        //}

        #endregion

        #region Encryption

        public static byte[] EncryptedData { get; set; }

        public static void BeginEcryptWrite()
        {
            ICryptoTransform ct = AesEninge.CreateEncryptor(AesEninge.Key, AesEninge.IV);

            Clear();

            ms = new MemoryStream();
            cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
            bw = new BinaryWriter(cs);
        }

        public static int EncryptWrite(byte[] RawData, int Offset, int Len)
        {
            if (AesEninge.isNull())
                throw new ArgumentNullException("Must call init frist!");
            if (RawData.isNull() || RawData.Length < 1)
                throw new ArgumentNullException("RawData");
            if (Len > RawData.Length)
                throw new ArgumentOutOfRangeException("Len");

            try
            {
                bw.Write(RawData, Offset, Len);
            }
            catch
            {
                return 0;
            }

            return ms.Length.ToInt();
        }

        public static void EndEcryptWrite()
        {
            bw.Close();
            bw.Dispose();

            cs.Close();
            cs.Dispose();

            ms.Close();

            EncryptedData = ms.ToArray();

            ms.Dispose();
        }

        #region Old Method(s)

        //public static byte[] Encrypt(byte[] RawData)
        //{
        //    if (AesEninge.isNull())
        //        throw new ArgumentNullException("Must call init frist!");
        //    if (RawData.isNull() || RawData.Length < 1)
        //        throw new ArgumentNullException("RawData");

        //    MemoryStream ms = null;
        //    CryptoStream cs = null;
        //    BinaryWriter bw = null;

        //    try
        //    {
        //        ICryptoTransform ct = AesEninge.CreateEncryptor(AesEninge.Key, AesEninge.IV);
        //        ms = new MemoryStream();
        //        cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
        //        bw = new BinaryWriter(cs);

        //        bw.Write(RawData);
        //    }
        //    finally
        //    {
        //        if(bw.isNull() == false)
        //            bw.Close();
        //        if (cs.isNull() == false)
        //            cs.Close();
        //        if (ms.isNull() == false)
        //            ms.Close();
        //    }

        //    return ms.ToArray();
        //}

        #endregion

        #endregion

        #region Decryption

        public static byte[] DecryptedData { get; set; }

        public static void BeginDecryptRead()
        {
            ICryptoTransform ct = AesEninge.CreateDecryptor(AesEninge.Key, AesEninge.IV);

            Clear();

            ms = new MemoryStream();
            cs = new CryptoStream(ms, ct, CryptoStreamMode.Read);
            br = new BinaryReader(cs);
        }

        public static void DecryptRead(byte[] RawData, int Offset, int Len)
        {
            if (AesEninge.isNull())
                throw new ArgumentNullException("Must call init frist!");
            if (RawData.isNull() || RawData.Length < 1)
                throw new ArgumentNullException("RawData");
            if (Len > RawData.Length)
                throw new ArgumentOutOfRangeException("Len");

            try
            {
                br.Read(RawData, Offset, Len);
            }
            catch
            {
                return;
            }
        }

        public static void EndDecryptRead()
        {
            br.Close();
            br.Dispose();

            cs.Close();
            cs.Dispose();

            ms.Close();

            DecryptedData = ms.ToArray();

            ms.Dispose();
        }

        #region Old Method(s)

        //public static byte[] Decrypt(byte[] RawData)
        //{
        //    if (AesEninge.isNull())
        //        throw new ArgumentNullException("Must call init frist!");
        //    if (RawData.isNull() || RawData.Length < 1)
        //        throw new ArgumentNullException("RawData");

        //    MemoryStream ms = null;
        //    CryptoStream cs = null;
        //    BinaryReader br = null;
        //    byte[] dptData = null;

        //    try
        //    {
        //        ICryptoTransform ct = AesEninge.CreateDecryptor(AesEninge.Key, AesEninge.IV);
        //        ms = new MemoryStream(RawData);
        //        cs = new CryptoStream(ms, ct, CryptoStreamMode.Read);
        //        br = new BinaryReader(cs);

        //        dptData = br.ReadBytes(RawData.Length);
        //    }
        //    finally
        //    {
        //        if (br.isNull() == false)
        //            br.Close();
        //        if (cs.isNull() == false)
        //            cs.Close();
        //        if (ms.isNull() == false)
        //            ms.Close();
        //    }

        //    return dptData;
        //}

        #endregion

        #endregion

        #region Disposable

        public static void Clear()
        {
            if (ms.isNull() == false)
                ms.Dispose();

            if (cs.isNull() == false)
                cs.Dispose();

            if (bw.isNull() == false)
                bw.Dispose();

            if (br.isNull() == false)
                br.Dispose();
        }

        #endregion
    }
}
